/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "EcsClient.h"
#include "protect_engines/hcs/utils/HCSTokenMgr.h"

namespace {
const std::string MODULE_NAME = "EcsClient";
using Defer = std::shared_ptr<void>;
}

HCS_PLUGIN_NAMESPACE_BEGIN

bool EcsClient::CheckParams(ModelBase &model)
{
    if (!model.GetToken().empty()) {
        INFOLOG("Jump the param check when in the op service.");
        return true;
    }
    if (!model.UserInfoIsSet()) {
        ERRLOG("User info does not set.");
        return false;
    }
    if (!model.DomainIsSet()) {
        ERRLOG("Domain does not set.");
        return false;
    }
    if (!model.EndPointIsSet()) {
        ERRLOG("Endpoint does not set.");
        return false;
    }
    if (!model.ScopeValueIsSet()) {
        ERRLOG("Scope value does not set.");
        return false;
    }
    if (!model.SetRegionIsSet()) {
        ERRLOG("Region does not set.");
        return false;
    }
    return true;
}

// Reacquire token when token expired
bool EcsClient::UpdateToken(ModelBase &request, std::string &tokenStr)
{
    if (!HCSTokenMgr::GetInstance().ReacquireToken(request, tokenStr)) {
        ERRLOG("Get token failed.");
        return false;
    }
    return true;
}

std::shared_ptr<LockServerResponse> EcsClient::LockServer(LockServerRequest &request)
{
    if (!CheckParams(request) || !request.ServerIdIsSet()) {
        ERRLOG("Lock server request check params failed.");
        return nullptr;
    }
    RequestInfo requestInfo;
    requestInfo.m_method = "POST";
    requestInfo.m_resourcePath = "{endpoint}/servers/{serverId}/action";
    std::string endpoint;
    std::string tokenStr;
    Defer _(nullptr, [&](...) { Module::CleanMemoryPwd(tokenStr); });
    if (!HCSTokenMgr::GetInstance().GetToken(request, tokenStr, endpoint)) {
        ERRLOG("Get token failed.");
        return nullptr;
    }
    requestInfo.m_pathParams["endpoint"] = std::move(endpoint);
    requestInfo.m_pathParams["serverId"] = request.GetServerId();
    requestInfo.m_queryParams = {};
    requestInfo.m_headerParams["Content-Type"] = "application/json";
    requestInfo.m_headerParams["X-Auth-Token"] = std::move(tokenStr);
    // 准备请求body体
    Json::Value LockBody;
    Json::Value jsonReq;
    jsonReq["lock"] = LockBody;
    Json::FastWriter fastWriter;
    requestInfo.m_body = fastWriter.write(jsonReq);
    requestInfo.m_auth = request.GetIamUserInfo();
    std::shared_ptr<LockServerResponse> response = std::make_shared<LockServerResponse>();
    if (CallApi(requestInfo, response, request) != SUCCESS) {
        ERRLOG("Get lock server response failed, errorCode:%d, errorString:%s", response->GetErrCode(),
            response->GetErrString().c_str());
        return response;
    }
    return response;
}
std::shared_ptr<UnLockServerResponse> EcsClient::UnLockServer(UnLockServerRequest &request)
{
    if (!CheckParams(request) || !request.ServerIdIsSet()) {
        ERRLOG("UnLock server request check params failed.");
        return nullptr;
    }
    RequestInfo requestInfo;
    requestInfo.m_method = "POST";
    requestInfo.m_resourcePath = "{endpoint}/servers/{serverId}/action";
    std::string endpoint;
    std::string tokenStr;
    Defer _(nullptr, [&](...) { Module::CleanMemoryPwd(tokenStr); });
    if (!HCSTokenMgr::GetInstance().GetToken(request, tokenStr, endpoint)) {
        ERRLOG("Get token failed.");
        return nullptr;
    }
    requestInfo.m_pathParams["endpoint"] = std::move(endpoint);
    requestInfo.m_pathParams["serverId"] = request.GetServerId();
    requestInfo.m_queryParams = {};
    requestInfo.m_headerParams["Content-Type"] = "application/json";
    requestInfo.m_headerParams["X-Auth-Token"] = std::move(tokenStr);
    // 准备请求body体
    Json::Value LockBody;
    Json::Value jsonReq;
    jsonReq["unlock"] = LockBody;
    Json::FastWriter fastWriter;
    requestInfo.m_body = fastWriter.write(jsonReq);
    requestInfo.m_auth = request.GetIamUserInfo();
    std::shared_ptr<UnLockServerResponse> response = std::make_shared<UnLockServerResponse>();
    if (CallApi(requestInfo, response, request) != SUCCESS) {
        ERRLOG("Get unLock server response failed, errorCode:%d, errorString:%s", response->GetErrCode(),
            response->GetErrString().c_str());
        return response;
    }
    return response;
}

std::shared_ptr<GetServerListResponse> EcsClient::GetServerList(GetServerListRequest &request)
{
    if (!CheckParams(request)) {
        return nullptr;
    }
    RequestInfo requestInfo;
    requestInfo.m_method = "GET";
    requestInfo.m_resourcePath = "{endpoint}/servers";
    requestInfo.m_pathParams["domain"] = request.GetEndpoint();
    requestInfo.m_pathParams["regionId"] = request.GetRegion();
    requestInfo.m_auth = request.GetIamUserInfo();
    std::string endpoint;
    std::string tokenStr;
    Defer _(nullptr, [&](...) { Module::CleanMemoryPwd(tokenStr); });
    if (!HCSTokenMgr::GetInstance().GetToken(request, tokenStr, endpoint)) {
        ERRLOG("Get token failed.");
        return nullptr;
    }
    requestInfo.m_pathParams["endpoint"] = std::move(endpoint);
    requestInfo.m_headerParams["X-Auth-Token"] = std::move(tokenStr);
    requestInfo.m_headerParams["Content-Type"] = "application/json";
    if (request.ServerStatusIsSet()) {
        requestInfo.m_queryParams["status"] = request.GetServerStatus();
    }
    if (request.ServerIpIsSet()) {
        requestInfo.m_queryParams["ip"] = request.GetServerIp();
    }
    if (request.ServerNameIsSet()) {
        requestInfo.m_queryParams["name"] = request.GetServerName();
    }
    if (request.ServerOffsetIsSet()) {
        requestInfo.m_queryParams["offset"] = std::to_string(request.GetServerOffset());
    }
    if (request.ServerLimitIsSet()) {
        requestInfo.m_queryParams["limit"] = std::to_string(request.GetServerLimit());
    }
    std::shared_ptr<GetServerListResponse> response = std::make_shared<GetServerListResponse>();
    if (CallApi(requestInfo, response, request) != SUCCESS) {
        ERRLOG("Get server list response failed, errorCode:%d, errorString:%s", response->GetErrCode(),
            response->GetErrString().c_str());
        return response;
    }
    if (!response->Serial()) {
        ERRLOG("Get server list serial failed.");
        return nullptr;
    }
    return response;
}

std::shared_ptr<GetServerListResponse> EcsClient::GetServerDetailList(GetServerListRequest &request)
{
    if (!CheckParams(request)) {
        return nullptr;
    }
    RequestInfo requestInfo;
    requestInfo.m_method = "GET";
    requestInfo.m_resourcePath = "{endpoint}/servers/detail";
    requestInfo.m_pathParams["domain"] = request.GetEndpoint();
    requestInfo.m_pathParams["regionId"] = request.GetRegion();
    requestInfo.m_pathParams["projectId"] = request.GetScopeValue();
    requestInfo.m_auth = request.GetIamUserInfo();
    std::string endpoint;
    std::string tokenStr;
    Defer _(nullptr, [&](...) { Module::CleanMemoryPwd(tokenStr); });
    if (!HCSTokenMgr::GetInstance().GetToken(request, tokenStr, endpoint)) {
        ERRLOG("Get token failed.");
        return nullptr;
    }
    requestInfo.m_pathParams["endpoint"] = std::move(endpoint);
    requestInfo.m_headerParams["X-Auth-Token"] = std::move(tokenStr);
    requestInfo.m_headerParams["Content-Type"] = "application/json";
    if (request.ServerLimitIsSet()) {
        requestInfo.m_queryParams["limit"] = std::to_string(request.GetServerLimit());
    }
    if (request.ServerMarkerIsSet()) {
        requestInfo.m_queryParams["marker"] = request.GetServerMarker();
    }
    std::shared_ptr<GetServerListResponse> response = std::make_shared<GetServerListResponse>();
    if (CallApi(requestInfo, response, request) != SUCCESS) {
        ERRLOG("Get server list response failed, errorCode:%d, errorString:%s", response->GetErrCode(),
            response->GetErrString().c_str());
        return response;
    }
    if (!response->Serial()) {
        ERRLOG("Get server list serial failed.");
        return nullptr;
    }
    return response;
}

std::shared_ptr<GetServerDetailsResponse> EcsClient::GetServerDetails(GetServerDetailsRequest &request)
{
    if (!CheckParams(request) || !request.ServerIdIsSet()) {
        return nullptr;
    }
    RequestInfo requestInfo;
    requestInfo.m_method = "GET";
    requestInfo.m_resourcePath = "{endpoint}/servers/{serverId}";
    std::string endpoint;
    std::string tokenStr;
    Defer _(nullptr, [&](...) { Module::CleanMemoryPwd(tokenStr); });
    if (!HCSTokenMgr::GetInstance().GetToken(request, tokenStr, endpoint)) {
        ERRLOG("Get token failed.");
        return nullptr;
    }
    requestInfo.m_pathParams["endpoint"] = std::move(endpoint);
    requestInfo.m_pathParams["projectId"] = request.GetScopeValue();
    requestInfo.m_pathParams["serverId"] = request.GetServerId();
    requestInfo.m_queryParams = {};
    requestInfo.m_headerParams["Content-Type"] = "application/json";
    requestInfo.m_headerParams["X-Auth-Token"] = std::move(tokenStr);
    requestInfo.m_auth = request.GetIamUserInfo();
    std::shared_ptr<GetServerDetailsResponse> response = std::make_shared<GetServerDetailsResponse>();
    if (CallApi(requestInfo, response, request) != SUCCESS) {
        ERRLOG("Get server details response failed, errorCode:%d, errorString:%s", response->GetErrCode(),
            response->GetErrString().c_str());
        return response;
    }
    if (!response->Serial()) {
        ERRLOG("Get server details serial failed.");
        return nullptr;
    }
    return response;
}

std::shared_ptr<PowerOffServerResponse> EcsClient::PowerOffServer(PowerOffServerRequest &request)
{
    if (!CheckParams(request) || !request.ServerIdIsSet()) {
        ERRLOG("Power off server request check params failed.");
        return nullptr;
    }
    RequestInfo requestInfo;
    requestInfo.m_method = "POST";
    requestInfo.m_resourcePath = "{endpoint}/servers/{serverId}/action";
    std::string endpoint;
    std::string tokenStr;
    Defer _(nullptr, [&](...) { Module::CleanMemoryPwd(tokenStr); });
    if (!HCSTokenMgr::GetInstance().GetToken(request, tokenStr, endpoint)) {
        ERRLOG("Get token failed.");
        return nullptr;
    }
    requestInfo.m_pathParams["endpoint"] = std::move(endpoint);
    requestInfo.m_pathParams["serverId"] = request.GetServerId();
    requestInfo.m_queryParams = {};
    requestInfo.m_headerParams["Content-Type"] = "application/json";
    requestInfo.m_headerParams["X-Auth-Token"] = std::move(tokenStr);
    // 准备请求body体
    Json::Value stopType;
    stopType["type"] = "SOFT";
    Json::Value jsonReq;
    jsonReq["os-stop"] = stopType;
    Json::FastWriter fastWriter;
    requestInfo.m_body = fastWriter.write(jsonReq);
    requestInfo.m_auth = request.GetIamUserInfo();
    std::shared_ptr<PowerOffServerResponse> response = std::make_shared<PowerOffServerResponse>();
    if (CallApi(requestInfo, response, request) != SUCCESS) {
        ERRLOG("Get Power off server response failed, errorCode:%d, errorString:%s", response->GetErrCode(),
            response->GetErrString().c_str());
        return response;
    }
    return response;
}

std::shared_ptr<PowerOnServerResponse> EcsClient::PowerOnServer(PowerOnServerRequest &request)
{
    if (!CheckParams(request) || !request.ServerIdIsSet()) {
        ERRLOG("Power on server request check params failed.");
        return nullptr;
    }
    RequestInfo requestInfo;
    requestInfo.m_method = "POST";
    requestInfo.m_resourcePath = "{endpoint}/servers/{serverId}/action";
    std::string endpoint;
    std::string tokenStr;
    Defer _(nullptr, [&](...) { Module::CleanMemoryPwd(tokenStr); });
    if (!HCSTokenMgr::GetInstance().GetToken(request, tokenStr, endpoint)) {
        ERRLOG("Get token failed.");
        return nullptr;
    }
    requestInfo.m_pathParams["endpoint"] = std::move(endpoint);
    requestInfo.m_pathParams["serverId"] = request.GetServerId();
    requestInfo.m_queryParams = {};
    requestInfo.m_headerParams["Content-Type"] = "application/json";
    requestInfo.m_headerParams["X-Auth-Token"] = std::move(tokenStr);
    // 准备请求body体
    Json::Value startType;
    Json::Value jsonReq;
    jsonReq["os-start"] = startType;
    Json::FastWriter fastWriter;
    requestInfo.m_body = fastWriter.write(jsonReq);
    requestInfo.m_auth = request.GetIamUserInfo();
    std::shared_ptr<PowerOnServerResponse> response = std::make_shared<PowerOnServerResponse>();
    if (CallApi(requestInfo, response, request) != SUCCESS) {
        ERRLOG("Get Power on server response failed, errorCode:%d, errorString:%s", response->GetErrCode(),
            response->GetErrString().c_str());
        return response;
    }
    return response;
}

std::shared_ptr<DetachServerVolumeResponse> EcsClient::DetachServerVolume(DetachServerVolumeRequest &request)
{
    if (!CheckParams(request) || !request.ServerIdIsSet() || !request.VolumeIdIsSet()) {
        ERRLOG("Detach server request check params failed.");
        return nullptr;
    }
    RequestInfo requestInfo;
    requestInfo.m_method = "DELETE";
    requestInfo.m_resourcePath = "{endpoint}/servers/{serverId}/os-volume_attachments/{volumeId}";
    std::string endpoint;
    std::string tokenStr;
    Defer _(nullptr, [&](...) { Module::CleanMemoryPwd(tokenStr); });
    if (!HCSTokenMgr::GetInstance().GetToken(request, tokenStr, endpoint)) {
        ERRLOG("Get token failed.");
        return nullptr;
    }
    requestInfo.m_pathParams["endpoint"] = std::move(endpoint);
    requestInfo.m_pathParams["serverId"] = request.GetServerId();
    requestInfo.m_pathParams["volumeId"] = request.GetVolumeId();
    requestInfo.m_queryParams = {};
    requestInfo.m_headerParams["Content-Type"] = "application/json";
    requestInfo.m_headerParams["X-Auth-Token"] = std::move(tokenStr);
    requestInfo.m_auth = request.GetIamUserInfo();
    std::shared_ptr<DetachServerVolumeResponse> response = std::make_shared<DetachServerVolumeResponse>();
    if (CallApi(requestInfo, response, request) != SUCCESS) {
        ERRLOG("Get detach server response failed, errorCode:%d, errorString:%s", response->GetErrCode(),
            response->GetErrString().c_str());
        return response;
    }
    return response;
}

std::shared_ptr<AttachServerVolumeResponse> EcsClient::AttachServerVolume(AttachServerVolumeRequest &request)
{
    if (!CheckParams(request) || !request.ServerIdIsSet() || !request.VolumeIdIsSet()) {
        ERRLOG("Attach server request check params failed.");
        return nullptr;
    }
    RequestInfo requestInfo;
    requestInfo.m_method = "POST";
    requestInfo.m_resourcePath = "{endpoint}/servers/{serverId}/os-volume_attachments";
    std::string endpoint;
    std::string tokenStr;
    Defer _(nullptr, [&](...) { Module::CleanMemoryPwd(tokenStr); });
    if (!HCSTokenMgr::GetInstance().GetToken(request, tokenStr, endpoint)) {
        ERRLOG("Get token failed.");
        return nullptr;
    }
    requestInfo.m_pathParams["endpoint"] = std::move(endpoint);
    requestInfo.m_pathParams["serverId"] = request.GetServerId();
    requestInfo.m_queryParams = {};
    requestInfo.m_headerParams["Content-Type"] = "application/json";
    requestInfo.m_headerParams["X-Auth-Token"] = std::move(tokenStr);
    // 准备请求body体
    Json::Value volumeBody;
    volumeBody["volumeId"] = request.GetVolumeId();
    if (request.DeviceIsSet()) {
        volumeBody["device"] = request.GetDevice();
    }
    Json::Value jsonReq;
    jsonReq["volumeAttachment"] = volumeBody;
    Json::FastWriter fastWriter;
    requestInfo.m_body = fastWriter.write(jsonReq);
    requestInfo.m_auth = request.GetIamUserInfo();
    std::shared_ptr<AttachServerVolumeResponse> response = std::make_shared<AttachServerVolumeResponse>();
    if (CallApi(requestInfo, response, request) != SUCCESS) {
        ERRLOG("Get Attach server response failed, errorCode:%d, errorString:%s", response->GetErrCode(),
            response->GetErrString().c_str());
        return response;
    }
    return response;
}

HCS_PLUGIN_NAMESPACE_END